package com.jsb.laundry.common.design_service.pay.strategy.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.jsb.laundry.common.design_service.pay.PayStrategy;
import com.jsb.laundry.common.design_service.pay.properties.WechatAPPPayProperties;
import com.jsb.laundry.common.design_service.pay.properties.WechatMiniPayProperties;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAConfig;
import com.wechat.pay.java.service.payments.app.AppService;
import com.wechat.pay.java.service.payments.app.AppServiceExtension;
import com.wechat.pay.java.service.payments.app.model.Amount;
import com.wechat.pay.java.service.payments.app.model.PrepayRequest;
import com.wechat.pay.java.service.payments.app.model.PrepayResponse;
import com.wechat.pay.java.service.payments.app.model.QueryOrderByOutTradeNoRequest;
import com.wechat.pay.java.service.payments.model.Transaction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.PrivateKey;
import java.security.Signature;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Base64;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

import static com.jsb.laundry.common.design_service.pay.properties.WechatMiniPayProperties.privateKeyPath;
import static com.jsb.laundry.common.design_service.pay.properties.WechatMiniPayProperties.wechatPayCertificatePath;

/**
 * 微信支付-APP
 */
@Service("JSB_WECHAT_APP_PAY")
public class WechatAppPayStrategy implements PayStrategy {

    private static final Logger LOGGER = LoggerFactory.getLogger(WechatAppPayStrategy.class);

    private final static DateTimeFormatter formatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME;
    private static Config config;

    private static WechatAPPPayProperties wechatAPPPayProperties;
    @Autowired
    public WechatAppPayStrategy(WechatAPPPayProperties wechatAPPPayProperties) {
        WechatAppPayStrategy.wechatAPPPayProperties = wechatAPPPayProperties;
    }
//    @Resource
//    private WechatAPPPayProperties wechatAPPPayProperties;

    @PostConstruct
    public void init() {
        config = new RSAConfig.Builder()
                .merchantId(wechatAPPPayProperties.getMerchantId())
                .privateKeyFromPath(privateKeyPath)
                .merchantSerialNumber(wechatAPPPayProperties.getMerchantSerialNumber())
                .wechatPayCertificatesFromPath(wechatPayCertificatePath)
                .build();
    }

    /**
     * 定义一个支付行为
     *
     * @param userId 用户标识
     * @param payId  支付单号
     * @param total  支付金额
     * @param attach 额外携带的参数
     * @return
     */
    @Override
    public String pay(String userId, String payId, int total, String attach) {
        JSONObject attachObject = JSONObject.parseObject(attach);
        AppService service = new AppService.Builder().config(config).build();
        PrepayRequest request = new PrepayRequest();
        Amount amount = new Amount();
        amount.setTotal(total);
        request.setAmount(amount);
        request.setAppid(wechatAPPPayProperties.getAppId());
        request.setMchid(wechatAPPPayProperties.getMerchantId());
        request.setDescription("聚洗衣APP支付");
        request.setTimeExpire(formatter.format(ZonedDateTime.now().plusMinutes(30))); //30分钟过期
        request.setNotifyUrl(attachObject.getString(WechatMiniPayProperties.callbackUrl));//回调地址
        request.setOutTradeNo(payId);
        request.setAttach(attachObject.getString("productType")); //额外携带参数


        PrepayResponse prepay = service.prepay(request);
        String prepayId = prepay.getPrepayId();
        LOGGER.info("用户id:[{}]微信APP支付拉起;支付类型:[{}];支付id:[{}];支付金额:[{}分];prepayId:[{}]", userId, attach, payId, total, prepayId);
        String signature = getSignature(prepayId);
        return signature;
    }

    /**
     * 查询订单
     *
     * @param orderId
     * @return
     */
    public String queryOrderByOutTradeNo(String orderId) {
        AppServiceExtension service = new AppServiceExtension.Builder().config(config).build();
        QueryOrderByOutTradeNoRequest queryOrderByOutTradeNoRequest = new QueryOrderByOutTradeNoRequest();
        queryOrderByOutTradeNoRequest.setMchid(wechatAPPPayProperties.getMerchantId());
        queryOrderByOutTradeNoRequest.setOutTradeNo(orderId);
        Transaction transaction = service.queryOrderByOutTradeNo(queryOrderByOutTradeNoRequest);
        String queryRes = JSONObject.toJSONString(transaction);
        LOGGER.info("订单id:[{}];查询订单信息结果:[{}]", orderId,queryRes);
        return queryRes;
    }

//    @Override
//    public String pay(String userId, String payId, int total, String attach) {
//        JSONObject attachObject = JSONObject.parseObject(attach);
//        AppServiceExtension service = new AppServiceExtension.Builder().config(config).build();
//        PrepayRequest request = new PrepayRequest();
//        Amount amount = new Amount();
//        amount.setTotal(total);
//        request.setAmount(amount);
//        request.setAppid(wechatMiniPayProperties.getAppId());
//        request.setMchid(wechatMiniPayProperties.getMerchantId());
//        request.setDescription("聚洗衣APP支付");
//        request.setTimeExpire(formatter.format(ZonedDateTime.now().plusMinutes(30))); //30分钟过期
//        request.setNotifyUrl(attachObject.getString(WechatMiniPayProperties.callbackUrl));//回调地址
//        request.setOutTradeNo(payId);
//        request.setAttach(attachObject.getString("productType")); //额外携带参数
//        PrepayWithRequestPaymentResponse prepayWithRequestPaymentResponse = service.prepayWithRequestPayment(request);
//        String jsonString = JSONObject.toJSONString(prepayWithRequestPaymentResponse);
//        return jsonString;
//    }

    /**
     * 根据聚申宝的支付ID,生成签名从而拉起支付
     *
     * @param prepayId
     * @return
     */
    private String getSignature(String prepayId) {
        String nonceStr = UUID.randomUUID().toString();
        long timestamp = System.currentTimeMillis() / 1000;
        String message = buildMessage(wechatAPPPayProperties.getAppId(), timestamp, nonceStr, prepayId);
        String signature = null;
        try {
            signature = sign(message.getBytes(StandardCharsets.UTF_8));
        } catch (Exception e) {
            e.printStackTrace();
        }
        Map<String, Object> map = new ConcurrentHashMap<>();
        map.put("appid", wechatAPPPayProperties.getAppId());
        map.put("partnerid", wechatAPPPayProperties.getMerchantId());
        map.put("prepayid", prepayId);
        map.put("timestamp", timestamp);
        map.put("noncestr", nonceStr);
        map.put("package", "Sign=WXPay");
        map.put("sign", signature);
        return JSON.toJSONString(map);
    }

    private static String buildMessage(String appId, long timestamp, String nonceStr, String prepayId) {
        StringBuffer result = new StringBuffer();
        result.append(appId).append("\n").
                append(timestamp).append("\n").
                append(nonceStr).append("\n").
                append(prepayId).append("\n");
        return String.valueOf(result);
    }

    /**
     * 根据私钥进行签名Base64解密
     *
     * @param message
     * @return
     * @throws Exception
     */
    private String sign(byte[] message) throws Exception {
        Signature sign = Signature.getInstance("SHA256withRSA");
        sign.initSign(getPrivateKey());
        sign.update(message);
        return Base64.getEncoder().encodeToString(sign.sign());
    }

    /**
     * 获取私钥对象
     *
     * @return
     * @throws IOException
     */
    private PrivateKey getPrivateKey() throws IOException {
        PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(
                new FileInputStream(privateKeyPath));
        return merchantPrivateKey;
    }

}
